 // -*- C++ -*-
#ifndef SUTILITY_H
#define SUTILITY_H

#include <string>
#include <cassert>

namespace sds {
  template <typename T, typename F> 
  struct _SConverter; 
}

#include "sattribute.h"
#include "sdatatype.h"

namespace sds {

  template <typename T, typename F> 
  struct _SConverter {
    typedef F argument_type;
    typedef T result_type;
    result_type operator()(const argument_type& x) {
      return (result_type)x;
    }
  };
  
  template <typename SDataTypeList>
  struct _SDecode {
    typedef typename SDataTypeList::Type Type;
    static const SDataType type = SDataTypeList::type;
    typedef typename SDataTypeList::Next Next;

    template <typename Iterator, typename Reader>
    void read(Iterator it, const Reader& reader) {
      if (reader.getDataType() == type) {
	Type* buffer = 0;
	try {
	  typedef typename std::iterator_traits<Iterator>::value_type T;
	  buffer = new Type[reader.getLength()];
	  reader.read((void*)buffer);
	  std::transform(buffer, buffer + reader.getLength(), 
			 it, _SConverter<T, Type>());
	  delete[] buffer;
	} catch (...) {
	  if (buffer) delete[] buffer;
	  throw;
	}
      } else {
	typedef typename SDataTypeList::Next Next;
	_SDecode<Next>().read(it, reader);
      }      
    }

  };

  template <>
  struct _SDecode<SDataTypeEmptyList> {

    template <typename Iterator, typename Reader>
    void read(Iterator, const Reader&) {
      assert(false);
    }
  };


  template <typename SDataTypeList>
  struct _SEncode {
    typedef typename SDataTypeList::Type Type;
    static const SDataType type = SDataTypeList::type;
    typedef typename SDataTypeList::Next Next;

    template <typename Iterator, typename Writer>
    void write(Iterator it, const Writer& writer) {
      if (writer.getDataType() == type) {
	Type* buffer = 0;
	try {
	  buffer = new Type[writer.getLength()];
	  std::copy(it, it + writer.getLength(), buffer);
	  writer.write((void*)buffer);
	  delete[] buffer;
	} catch (...) {
	  if (buffer) delete[] buffer;
	  throw;
	}
      } else {
	typedef typename SDataTypeList::Next Next;
	_SEncode<Next>().write(it, writer);
      }      
    }

  };

  template <>
  struct _SEncode<SDataTypeEmptyList> {
    template <typename Iterator, typename Writer>
    void write(Iterator, const Writer&) {
      assert(false);
    }
  };

  

  std::string SAttributeToString(const SAttribute& attribute);
  std::string SDataTypeToString(const SDataType& type);

}

#endif
